package com.tianji.promotion.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.*;
import com.tianji.promotion.domain.dto.CouponFormDTO;
import com.tianji.promotion.domain.dto.CouponIssueFormDTO;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.CouponScope;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.domain.query.CouponQuery;
import com.tianji.promotion.domain.vo.CouponDetailVO;
import com.tianji.promotion.domain.vo.CouponPageVO;
import com.tianji.promotion.domain.vo.CouponScopeVO;
import com.tianji.promotion.domain.vo.CouponVO;
import com.tianji.promotion.enums.CouponScopeStatus;
import com.tianji.promotion.enums.CouponStatus;
import com.tianji.promotion.enums.DiscountType;
import com.tianji.promotion.enums.ObtainType;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.service.ICouponScopeService;
import com.tianji.promotion.service.ICouponService;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.tianji.common.constants.CacheConstant.COUPON;

/**
 * <p>
 * 优惠券的规则信息 服务实现类
 * </p>
 *
 * @author xjs
 */
@Service
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {

    @Autowired
    private ICouponScopeService scopeService;

    @Autowired
    private IExchangeCodeService codeService;

    @Autowired
    private CatalogueClient catalogueClient;

    @Autowired
    private IUserCouponService userCouponService;

    @Autowired
    private StringRedisTemplate redisTemplate;
    /**
     * 查询系统中的优惠券
     * 1）发放中,不能过期的
     * 2）手动领取
     * */
    @Override
    public List<CouponVO> couponList() {
        LambdaQueryWrapper<Coupon> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Coupon::getStatus, CouponStatus.ISSUING.getValue());
        queryWrapper.eq(Coupon::getObtainWay, ObtainType.PUBLIC.getValue());
        List<Coupon> couponList = this.list(queryWrapper);
        if(CollUtil.isEmpty(couponList)){
            return null;
        }
        List<CouponVO> voList = BeanUtil.copyToList(couponList, CouponVO.class);
        // 获取用户信息
        Long userId = UserContext.getUser();
        if(userId == null){
            // 说经用户没有登陆
            for (CouponVO couponVO : voList) {
                couponVO.setReceived(false);
                couponVO.setAvailable(true);
            }
            return voList;
        }
        // 查询用户领券列表
        List<UserCoupon> userCouponList = userCouponService.lambdaQuery()
                .eq(UserCoupon::getUserId, userId)
                .in(UserCoupon::getStatus, Arrays.asList(1,2))
                .list();
        if(CollUtil.isEmpty(userCouponList)){
            userCouponList = new ArrayList<>();
        }
        Map<Long, List<UserCoupon>> userCouponMap = userCouponList.stream().collect(Collectors.groupingBy(UserCoupon::getCouponId));
        for (CouponVO couponVO : voList) {
            Integer userLimit = couponVO.getUserLimit();
            List<UserCoupon> userCoupons = userCouponMap.get(couponVO.getId());
            int takeCount = CollUtils.isEmpty(userCoupons)?0:userCoupons.size();
            // 是否可领取，用户已经领用数量 《 每个用户最大限领数量 && 券的总的已经领用数量 《 券的最大数量
            couponVO.setAvailable(takeCount < userLimit && couponVO.getIssueNum() < couponVO.getTotalNum());
            // 是否可以使用，已经领取 && 没有使用 数量  》 0
            int unusedCount = (int)(CollUtils.isEmpty(userCoupons)?0:userCoupons.stream().map(uc->Objects.equals(uc.getStatus(), 1)).count());
            couponVO.setReceived(unusedCount > 0);
        }
        return voList;
    }

    @Override
    public void issue(Long id, CouponIssueFormDTO dto) {
        // 根据id查询coupon
        Coupon couponDB = this.getById(id);
        if(couponDB == null){
            throw new BizIllegalException("优惠券不存在");
        }
        if(dto.getIssueBeginTime() == null){
            dto.setIssueBeginTime(LocalDateTime.now());
        }
        // 发放
        Coupon coupon = BeanUtil.copyProperties(dto, Coupon.class);
        LocalDateTime issueBeginTime = dto.getIssueBeginTime();
        LocalDateTime now = LocalDateTime.now();
        Integer status = null;
        if(issueBeginTime.isAfter(now)){
            status = CouponStatus.UN_ISSUE.getValue();
        }else{
            status = CouponStatus.ISSUING.getValue();
        }
        coupon.setStatus(status);
        coupon.setUpdateTime(LocalDateTime.now());
        this.updateById(coupon);
        // 存入redis缓存
        Coupon lastestCoupon = this.getById(id);
        String key = COUPON + id;
        redisTemplate.opsForValue().set(key, JsonUtils.toJsonStr(lastestCoupon));
        // 兑换码
        Integer obtainWay = couponDB.getObtainWay();
        if(!Objects.equals(ObtainType.ISSUE.getValue(), obtainWay) ||
                !Objects.equals(CouponStatus.DRAFT.getValue(),couponDB.getStatus())){
            return;
        }
        //异步生成兑换码
        codeService.asyncGenerateCode(id, dto.getIssueEndTime(), couponDB.getTotalNum());
    }

    @Override
    public PageDTO<CouponPageVO> pageQuery(CouponQuery query) {
        Page<Coupon> page = query.toMpPage("create_time", false);
        LambdaQueryWrapper<Coupon> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(query.getStatus()!=null, Coupon::getStatus, query.getStatus())
                .eq(DiscountType.of(query.getType())!=null , Coupon::getDiscountType, query.getType())
                .like(StringUtils.isNotBlank(query.getName()), Coupon::getName, query.getName());
        this.page(page, queryWrapper);
        List<Coupon> records = page.getRecords();
        if(CollUtil.isEmpty(records)){
            return PageDTO.empty(page);
        }
        List<CouponPageVO> voList = BeanUtil.copyToList(records, CouponPageVO.class);
        return PageDTO.of(page, voList);
    }

    @Override
    public void addCoupon(CouponFormDTO dto) {
//        Long userId = UserContext.getUser();
        // dto -> coupon， status=1
        Coupon coupon = BeanUtil.copyProperties(dto, Coupon.class);
        coupon.setStatus(CouponStatus.DRAFT.getValue());
        coupon.setDiscountType(dto.getDiscountType().getValue());
        coupon.setObtainWay(dto.getObtainWay().getValue());
//        coupon.setCreateTime(LocalDateTime.now());
//        coupon.setUpdateTime(LocalDateTime.now());
//        coupon.setCreater(userId);
//        coupon.setUpdater(userId);
        // 保存到coupon表
        this.save(coupon);
        // 判断是否限定了范围
        if(!dto.getSpecific()){
            return;
        }
        // 如果是，保存到coupon_scope表
        List<Long> scopes = dto.getScopes();
        if(CollUtil.isEmpty(scopes)){
            throw new BadRequestException("范围不能为空");
        }
        List<CouponScope> scopeList = new ArrayList<>(scopes.size());
        for (Long scope : scopes) {
            CouponScope couponScope = new CouponScope();
            couponScope.setType(CouponScopeStatus.CATEGORY.getValue());
            couponScope.setBizId(scope);
            couponScope.setCouponId(coupon.getId());
            scopeList.add(couponScope);
        }
        scopeService.saveBatch(scopeList);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCoupon(Long id, CouponFormDTO dto) {
        Coupon couponDB = this.getById(id);
        AssertUtils.isNotNull(couponDB, "优惠券不存在");
        CouponStatus status = CouponStatus.of(couponDB.getStatus());
        if (status != CouponStatus.DRAFT) {
            throw new BizIllegalException("只能修改未发放的优惠券");
        }
        Coupon couponUpdate = BeanUtils.copyBean(dto, Coupon.class);
        couponUpdate.setId(id);
        this.updateById(couponUpdate);
        // 判断使用范围
        if (couponDB.getSpecific() != null && couponDB.getSpecific()) {
            // 如果是限定了范围，删除旧的范围
            scopeService.deleteByCouponId(id);
        }
        if(dto.getSpecific() != null && dto.getSpecific()){
            List<Long> categoryIds = dto.getScopes();
            // 保存到coupon_scope表
            List<CouponScope> scopes = new ArrayList<>();
            for (Long categoryId : categoryIds) {
                CouponScope scope = new CouponScope();
                scope.setType(1);
                scope.setCouponId(id);
                scope.setBizId(categoryId);
                scopes.add(scope);
            }
            scopeService.saveBatch(scopes);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteCoupon(Long id) {
        Coupon couponDB = this.getById(id);
        AssertUtils.isNotNull(couponDB, "优惠券不存在");
        CouponStatus status = CouponStatus.of(couponDB.getStatus());
        if (status != CouponStatus.DRAFT) {
            throw new BizIllegalException("只能修改未发放的优惠券");
        }
        // 删除优惠券
        this.removeById(id);
        // 删除范围
        Boolean specific = couponDB.getSpecific();
        if(specific != null && specific){
            scopeService.deleteByCouponId(id);
        }
        // 删除兑换码
        Integer obtainWay = couponDB.getObtainWay();
        ObtainType obtainType = ObtainType.of(obtainWay);
        if(obtainType == ObtainType.ISSUE){
            codeService.deleteByCouponId(id);
        }
    }

    @Override
    public CouponDetailVO getCoupon(Long id) {
        Coupon couponDB = this.getById(id);
        AssertUtils.isNotNull(couponDB, "优惠券不存在");
        CouponDetailVO vo = BeanUtils.copyBean(couponDB, CouponDetailVO.class);
        List<CouponScope> scopes = scopeService.getByCouponId(id);
        if(!CollUtils.isEmpty(scopes)){
            List<CouponScopeVO> scopeVoList = BeanUtils.copyList(scopes, CouponScopeVO.class);
            List<Long> categoryIds = scopeVoList.stream().map(v -> v.getId()).collect(Collectors.toList());
            List<CataSimpleInfoDTO> catalogList = catalogueClient.batchQueryCatalogue(categoryIds);
            Map<Long, String> catalogMap = catalogList.stream().collect(Collectors.toMap(CataSimpleInfoDTO::getId, v -> v.getName()));
            scopeVoList.stream().forEach(v->v.setName(catalogMap.get(v.getId())));
            vo.setScopes(scopeVoList);
        }
        return vo;
    }

    @Override
    public void pause(Long id) {
        Coupon couponDB = this.getById(id);
        AssertUtils.isNotNull(couponDB, "优惠券不存在");
        CouponStatus status = CouponStatus.of(couponDB.getStatus());
        if (status != CouponStatus.ISSUING) {
            throw new BizIllegalException("只能暂停发放中的优惠券");
        }
        Coupon couponUpdate = new Coupon();
        couponUpdate.setId(id);
        couponUpdate.setStatus(CouponStatus.PAUSE.getValue());
        couponUpdate.setUpdateTime(LocalDateTime.now());
        this.updateById(couponUpdate);
        // 删除缓存
        // 存入redis缓存
        String key = COUPON + id;
        redisTemplate.delete(key);
    }
}
